import Vue from 'vue'
import * as RongIMLib from '@rongcloud/imlib-next'
import {
  installer as rtcInstaller,
  RCRTCClient,
  RCTrack,
  RCFrameRate,
  RCResolution
} from '@rongcloud/plugin-rtc'
import {
  installer as callInstaller,
  RCCallClient,
  RCCallSession,
  RCCallErrorCode,
  IDeviceChangeParams,
  ISessionListener,
  IEndSummary,
  ISenderInfo,
  IMuteUser,
  IInvitedUsers,
  RCCallLanguage,
  RCCallEndReason,
  RCCallMediaType,
  IOfflineRecord,
  RCCallSessionState
} from '@rongcloud/plugin-call'
import Base from '@/im/base'
const { Message_Base, Conversation_Base, IM_Base } = Base
import { getImTokenApi } from '@/api/im'
import store from '@/store'

class Message extends Message_Base {
  constructor(_message) {
    if (_message.messageType === 'RC:IWNormalMsg') {
      const { msgType } = _message.content
      _message.messageType = msgType
    }

    super(_message)

    const {
      messageType,
      content: { content, remoteUrl, duration }
    } = _message

    if (this.isTextMessage(messageType)) {
      this.content = content
    }

    if (this.isVoiceMessage(messageType)) {
      this.content = remoteUrl
      this.duration = parseInt(duration)
    }

    if (this.isCallInviteVoiceMessage(messageType)) {
      this.content = '[语音通话]'
    }

    if (this.isCallInviteVideoMessage(messageType)) {
      this.content = '[视频通话]'
    }
  }
}

class Conversation extends Conversation_Base {
  constructor(_conversation) {
    super(_conversation)

    const { unreadMessageCount = 0, messages = [] } = _conversation
    this.unreadMessageCount = unreadMessageCount
    this.messages = messages
  }
}

class IM extends IM_Base {
  constructor(Vue) {
    super(Vue)
  }

  // 初始化融云IM对象
  create(appKey = '', opt = {}) {
    if (!appKey) return

    // 初始化IMLib
    RongIMLib.init({
      appkey: appKey
    })

    const CallInviteVoiceMessage = RongIMLib.registerMessageType(
      'Call:InviteVoice',
      true,
      true,
      [],
      false
    )
    const CallInviteVideoMessage = RongIMLib.registerMessageType(
      'Call:InviteVideo',
      true,
      true,
      [],
      false
    )
    this.CallInviteVoiceMessage = CallInviteVoiceMessage
    this.CallInviteVideoMessage = CallInviteVideoMessage

    // 初始化RTCLib
    const rtcClient = RongIMLib.installPlugin(rtcInstaller)

    // 初始化CallLib
    const caller = RongIMLib.installPlugin(callInstaller, {
      rtcClient,
      /**
       * @param {RCCallSession} session session 对象
       */
      onSession: (session) => {
        const {
          _stateMachine: { _targetId: targetId }
        } = session

        this.emit({
          type: this.IMEventType.CALL.onSession,
          data: {
            targetId
          },
          msg: '收到来电'
        })
        this.callSession = session
        session.registerSessionListener(this.getCallSessionEvent())
      },
      /**
       *  以下三条只要满足一条，就会触发onSessionClose
       *  1、本端用户自己主动挂断
       *  2、服务端把本端用户踢出 RTC 房间
       *  3、房间里小于2个人
       *  @param {RCCallSession} session 被结束的 session 对象
       *  @param summaryInfo 结束一个 session 的后汇总信息
       */
      onSessionClose: (session, summaryInfo) => {
        this.callSession = null
        this.emit({
          type: this.IMEventType.CALL.onSessionClose,
          data: summaryInfo,
          msg: '通话结束'
        })
      }
    })

    this.instance = RongIMLib
    this.caller = caller
    this.initListener()
  }

  // 销毁融云IM对象
  destroy() {
    RongIMLib.destroy()
  }

  // 连接融云服务器
  connect(
    params = {
      force: false
    }
  ) {
    const { force } = params

    if (this.connected && !force) return

    if (force) this.disconnect()

    getImTokenApi({
      type: 1
    }).then((res) => {
      const {
        data: { requestId, token, userId, reqBody }
      } = res
      store.commit('SET_USERID', userId)
      RongIMLib.connect(token).then((res) => {
        const { code, userId } = res

        if (code === RongIMLib.ErrorCode.SUCCESS) {
          this.connected = true

          store.dispatch('InitContacts', {
            imEngine: this
          })

          this.emit({
            type: this.IMEventType.SYSTEM.connected,
            data: {},
            msg: '已经链接到服务器'
          })
        }
      })
    })
  }

  // 断开链接
  disconnect() {
    RongIMLib.disconnect()
  }

  // 初始化监听器
  initListener(instance) {
    const Events = RongIMLib.Events
    const ErrorCode = RongIMLib.ErrorCode

    RongIMLib.addEventListener(Events.CONNECTING, () => {
      this.emit({
        type: this.IMEventType.SYSTEM.connecting,
        data: {},
        msg: '正在链接服务器'
      })
    })

    RongIMLib.addEventListener(Events.DISCONNECT, (code) => {
      switch (code) {
        case ErrorCode.RC_DISCONN_KICK:
          this.connected = false
          this.hungup()
          this.emit({
            type: this.IMEventType.SYSTEM.disconnected,
            data: {},
            msg: '已在其它设备登录'
          })
          break
        default:
          break
      }
    })

    RongIMLib.addEventListener(Events.MESSAGES, (evt) => {
      const { messages } = evt
      console.log('messages:', messages)
      if (Array.isArray(messages)) {
        messages.forEach((m) => {
          store.commit('UPDATE_CONVERSATION_Message_Received', new Message(m))
        })
      }
    })
  }

  // 获取指定会话
  getConversation(options) {
    const { conversationType, targetId, channelId } = options

    RongIMLib.getConversation({
      conversationType,
      targetId
    })
      .then(async (res) => {
        const { code, data } = res
        if (code === 0) {
          const { unreadMessageCount } = data
          const conversation = {
            conversationType,
            targetId
          }
          // 从当前时间开始向前查询
          const option = {
            timestamp: 0,
            count: unreadMessageCount > 10 ? unreadMessageCount : 10,
            order: 0
          }
          const { data: messages } = await this.getHistoryMessages(conversation, option)

          store.commit('SET_CONVERSATION', {
            key: targetId,
            value: new Conversation({
              conversationType,
              targetId,
              messages,
              unreadMessageCount
            })
          })
        }
      })
      .catch((e) => {
        console.error(e)
      })
  }

  // 获取某个会话的历史消息记录
  getHistoryMessages(conversation, option) {
    return new Promise((resolve) => {
      RongIMLib.getHistoryMessages(conversation, option).then((res) => {
        const { code, data } = res
        if (code === 0) {
          const { list, hasMore } = data
          resolve({
            data: list.map((m) => {
              return new Message(m)
            }),
            hasMore
          })
        } else {
          resolve({
            data: [],
            hasMore: false
          })
        }
      })
    })
  }

  // 清除指定会话未读数
  clearUnreadCount(conversationType, targetId) {
    RongIMLib.clearMessagesUnreadStatus({ conversationType, targetId }).then((res) => {
      if (res.code === 0) {
        store.commit('UPDATE_CONVERSATION_Unread_Message_Count', {
          targetId,
          unreadMessageCount: 0
        })
      }
    })
  }

  // 发送文本消息
  sendTextMessage(targetId, text, callback = () => {}) {
    if (!this.connected) {
      Vue.prototype.$alert('未连接', {
        confirmButtonText: '确定',
        callback: () => {
          callback(-1)
        }
      })
      return
    }

    const conversation = {
      conversationType: RongIMLib.ConversationType.PRIVATE,
      targetId
    }

    const message = new RongIMLib.TextMessage({
      content: text
    })

    const options = {
      onSendBefore: (message) => {
        // 消息发送前的回调
      }
    }

    // 发送消息
    RongIMLib.sendMessage(conversation, message, options).then((res) => {
      if (res.code === RongIMLib.ErrorCode.SUCCESS) {
        // console.log('消息发送成功', res.code, res.data)
        store.commit('UPDATE_CONVERSATION_Message_Sent', new Message(res.data))
      } else {
        // 消息发送失败
      }
      callback(res.code)
    })
  }

  // 发送语音消息
  sendVoiceMessage(targetId, file, duration, callback = () => {}) {
    if (!this.connected) {
      Vue.prototype.$alert('未连接', {
        confirmButtonText: '确定',
        callback: () => {
          callback(-1)
        }
      })
      return
    }

    const conversation = {
      conversationType: RongIMLib.ConversationType.PRIVATE,
      targetId
    }

    const msgBody = {
      file // 待上传文件
    }

    const hooks = {
      // 上传进度监听，可选
      onProgress(progress) {
        // console.log('progress =>', progress);
      },
      // 上传完成时的回调钩子，可选
      onComplete(fileInfo) {
        // console.log(fileInfo)
      }
    }

    const options = {
      contentDisposition: 'attachment' // 文件链接在浏览器中展示形式（仅 aws、stc 上传有效） 'inline': 在浏览器中预览， 'attachment': 直接下载。如果不传，html 类型文件会预览，其他类型为直接下载
    }

    RongIMLib.sendHQVoiceMessage(conversation, msgBody, hooks, options).then(
      ({ code, data: message }) => {
        if (code === 0) {
          store.commit('UPDATE_CONVERSATION_Message_Sent', new Message(message))
        } else {
          // 消息发送失败
        }

        callback(code)
      }
    )
  }

  getCallSessionEvent() {
    return {
      /**
       * 当远端用户已开始响铃，表示对方已收到呼叫请求
       * @param sender 已响铃的用户
       * @param session 当前的 session 对象
       */
      onRinging: (sender, session) => {
        const { userId } = sender

        this.emit({
          type: this.IMEventType.CALL.onRinging,
          data: sender,
          msg: `用户：${userId}已响铃`
        })
      },
      /**
       * 当远端用户同意接听
       * @param sender 远端用户
       * @param session 当前的 session 对象
       */
      onAccept: (sender, session) => {
        const { userId } = sender

        this.emit({
          type: this.IMEventType.CALL.onAccept,
          data: sender,
          msg: `用户：${userId}已同意接听`
        })
      },
      /**
       * 当有远端用户挂断
       * @param sender 远端用户
       * @param reason 挂断的原因
       * @param session 当前的 session 对象
       */
      onHungup: (sender, reason, session) => {
        const { userId } = sender

        this.emit({
          type: this.IMEventType.CALL.onHungup,
          data: {
            sender,
            reason
          },
          msg: `用户：${userId}已挂断`
        })
      },
      /**
       * 本端资源或远端资源已获取
       * @param track 本端资源或远端资源, track 不可设置成 Vue 组件的响应式数据
       * @param session 当前的 session 对象
       */
      onTrackReady: (track, session) => {
        // track.isLocalTrack() 是否为本地资源
        // track.isAudioTrack() 是否为音频
        // track.isVideoTrack() 是否为视频
        // track.getUserId()    产生该 track 的用户id

        // 播放音频。如果为远端音频，建议直接播放。如为本端音频，建议不播放，以减少回音。
        if (track.isAudioTrack() && !track.isLocalTrack()) {
          track.play()

          this.emit({
            type: this.IMEventType.CALL.onTrackReady,
            data: {},
            msg: '通话已建立'
          })
        }
      }
    }
  }

  // 发起单人通话
  async call(params, callback) {
    let targetId = typeof params === 'object' ? params.targetId : params

    if (window.android) {
      const { roomType, callerId, callerName, listenerId, listenerName } = params

      if (!callerId) {
        Vue.prototype.$popup.show({
          type: 'alert',
          success: (popup) => {
            popup.$on('confirm', () => {
              Vue.prototype.$popup.hide('alert')
            })
            popup.setData({
              title: '联系家长',
              content: '获取设备信息失败'
            })
          }
        })
        callback(false)
        return
      }

      if (!listenerId) {
        Vue.prototype.$popup.show({
          type: 'alert',
          success: (popup) => {
            popup.$on('confirm', () => {
              Vue.prototype.$popup.hide('alert')
            })
            popup.setData({
              title: '联系家长',
              content: '获取家长信息失败'
            })
          }
        })
        callback(false)
        return
      }

      let path = 'packages/main/pages/voip/index?'
      const query = {
        deviceType: 1, // 设备类型 1 => 智能学习机 2 => 校园电话
        businessType: 1, // 呼叫类型 1 => 硬件设备呼叫手机微信 2 => 手机微信呼叫硬件设备
        roomType, // 房间类型。voice: 音频房间；video: 视频房间
        miniprogramState: 'formal', // 接听方点击通知时打开的小程序类型 formal => 正式版; trial => 体验版; developer => 开发版
        callerId, // 拨打方id
        callerName: callerName || callerId, // 显示的拨打方名字，设备端发起通话时无效
        listenerId, // 接听方id
        listenerName: listenerName || listenerId, // 显示的接听方名字
        voipToken: '' // 拨打票据
      }
      // 拼接参数
      for (let key in query) {
        if (key != 'deviceType') {
          path += '&'
        }

        path += `${key}=${query[key]}`
      }

      /**
       * 启动微信小程序
       * @param_appid 为空启动默认小程序
       * @param_type 小程序类型 1 => 开发版，2 => 体验版，其它是正式版
       * @param_path 小程序启动路径
       * @param_mode 小程序展示方式（LandscapeMode） 1 => NORMAL 2 => LANDSCAPE 3 => LANDSCAPE_COMPAT 4 => FORCE_COMPAT
       * @param_fullScreen 是否强制全屏 1 => 是 0 => 否
       * @param_callback 执行方法回调
       */
      android.wmpf_launchMiniProgram('', 0, path, 4, 0, 'wmpf_onLaunchMiniProgram')

      RongIMLib.sendMessage(
        {
          conversationType: RongIMLib.ConversationType.PRIVATE,
          targetId
        },
        roomType === 'voice'
          ? new this.CallInviteVoiceMessage('') // 语音通话
          : new this.CallInviteVideoMessage('') // 视频通话
      ).then((res) => {
        if (res.code === 0) {
          store.commit('UPDATE_CONVERSATION_Message_Sent', new Message(res.data))
        }
      })
    } else {
      if (!this.connected) {
        Vue.prototype.$alert('未连接', {
          confirmButtonText: '确定',
          callback: () => {}
        })
        return
      }

      const { code, session } = await this.caller.call({
        targetId,
        mediaType: 1,
        listener: this.getCallSessionEvent()
      })

      this.callSession = session

      if (code === RCCallErrorCode.SUCCESS) {
        this.emit({
          type: this.IMEventType.CALL.callSuccess,
          data: code,
          msg: `拨打成功`
        })

        callback(true)
      } else {
        this.emit({
          type: this.IMEventType.CALL.callFail,
          data: code,
          msg: `拨打失败`
        })

        callback(false)
      }

      RongIMLib.sendMessage(
        {
          conversationType: RongIMLib.ConversationType.PRIVATE,
          targetId
        },
        new this.CallInviteVoiceMessage('')
      ).then((res) => {
        if (res.code === 0) {
          store.commit('UPDATE_CONVERSATION_Message_Sent', new Message(res.data))
        }
      })
    }
  }

  // 接听当前 callSession
  async accept(callback) {
    if (!this.callSession) {
      this.emit({
        type: this.IMEventType.CALL.acceptSuccess,
        data: code,
        msg: `无正在进行中的通话`
      })
      return
    }

    const { code } = await this.callSession.accept()

    if (code === RCCallErrorCode.SUCCESS) {
      this.emit({
        type: this.IMEventType.CALL.acceptSuccess,
        data: code,
        msg: `接听成功`
      })
    } else {
      this.emit({
        type: this.IMEventType.CALL.acceptFail,
        data: code,
        msg: `接听失败`
      })
    }
  }

  // 挂断当前 callSession
  async hungup() {
    if (!this.callSession) {
      this.emit({
        type: this.IMEventType.CALL.hungupSuccess,
        data: 1,
        msg: `无正在进行中的通话`
      })
      return
    }

    if (window.android) {
      android.alert('hungup')
    } else {
      const { code } = await this.callSession.hungup()

      if (code === RCCallErrorCode.SUCCESS) {
        this.emit({
          type: this.IMEventType.CALL.hungupSuccess,
          data: code,
          msg: `挂断成功`
        })
      } else {
        this.emit({
          type: this.IMEventType.CALL.hungupFail,
          data: code,
          msg: `挂断失败`
        })
      }
    }
  }

  // 启用麦克风
  openMic() {
    if (!this.callSession) {
      return
    }

    const { android_call } = window
    if (android_call) {
      android.setMicrophone('1')
    } else {
    }
  }

  // 关闭麦克风
  closeMic() {
    if (!this.callSession) {
      return
    }

    const { android_call } = window
    if (android_call) {
      android.setMicrophone('0')
    } else {
    }
  }

  // 启用扬声器
  async openSpeaker() {
    if (!this.callSession) {
      return
    }

    const { android_call } = window
    if (android_call) {
      android.setSpeaker('1')
    } else {
      await this.callSession.enableAudioTrack()
    }
  }

  // 关闭扬声器
  async closeSpeaker() {
    if (!this.callSession) {
      return
    }

    const { android_call } = window
    if (android_call) {
      android.setSpeaker('0')
    } else {
      await this.callSession.disableAudioTrack()
    }
  }
}

export default {
  IM,
  Message,
  Conversation
}
